/** \file 
 *  \brief Archivo usado para SO Linux.
 *  Este archivo contiene la definicon de las funciones de 
 *  de acceso al puerto, destinadas a SO tipo Linux
 *
 */


#define _SERIAL_PORT_CPP_

#include <stdio.h>   /* Standard input/output definitions */
#include <string.h>  /* String function definitions */
#include <stdlib.h>
#include <errno.h>

#include "serialport.h"

#define INVALID_HANDLE_VALUE -1
#define NONE      0    
#define RTSCTS    1
#define HARD      1
#define XONXOFF   2
#define SOFT      2
    

/** \var int Open_Port(char COMx[], int flags = O_RDWR)
 *  \brief Abre el puerto de comunicaciones
 *  \param COMx es el puerto a abrir "/dev/ttyS0","/dev/ttyS1","/dev/ttyACM0","/dev/ttyUSB0",...
 *  \param flags indica el modo de apertura del puerto: =_RDWR, O_RDONLY ó O_WRONLY
 *  \return El manejador del puerto
 *  \ingroup HeaderLinux
 */
int Open_Port(const char COMx[], int flags)
{
    int fd; // File descriptor for the port

    fd = open(COMx, flags | O_NOCTTY );//| O_NDELAY);

    if (fd <0)	
    {
	printf("open_port:fd=%d: No se puede abrir %s\n",fd,COMx);
  perror(NULL);
	return INVALID_HANDLE_VALUE;
    }
    printf("open_port:fd=%d: Abierto puerto %s\n",fd,COMx);
    tcflush(fd, TCIOFLUSH);
    return (fd);
}

    
/** \var struct termios Get_Configure_Port(int fd)
 *  \brief Devuelve la configuracion Actual del Puerto serie 
 *  \param fd Es el manejador del puerto
 *  \return Una estructuracon struct termios con la configuracion actual del puerto
 *  \ingroup HeaderLinux
 */  
bool Get_Configure_Port(int fd, struct termios *oldtio)
{

    if(tcgetattr(fd,oldtio)!=0)  /* almacenamos la configuracion actual del puerto */
    {
	printf("Error Pidiendo configuracion de Puerto\n");
	return false;
    }
    return true;
}
    
    
/** \var struct termios Configure_Port(int fd,unsigned int BaudRate,char CharParity[])
 *  \brief Configura el puerto serie 
 *  \param fd Es el manejador del puerto 
 *  \param BaudRate Es la velocidad del puerto
 *  \param CharParity indica el numero de bits de la transmision "8N1","7E1","7O1","7S1" 
 *  \return Una estructuracon Bcd con la configuracion del puerto
 *  \ingroup HeaderLinux
 */    
bool Configure_Port(int fd,unsigned int BaudRate,const char *CharParity, struct termios * newtio)
{
    bzero(newtio, sizeof(struct termios));    //limpiamos struct para recibir los
    //nuevos parametros del puerto
    //tcflush(fd, TCIOFLUSH);

	
    //CLOCAL  : conexion local, sin control de modem
    //CREAD   : activa recepcion de caracteres
    newtio->c_cflag =CLOCAL | CREAD ;
	
    cfsetispeed(newtio,BaudRate);
    cfsetospeed(newtio,BaudRate);
	
    if(strncmp(CharParity,"8N1",3)==0)	//CS8     : 8n1 (8bit,no paridad,1 bit de parada)
    {
	newtio->c_cflag &= ~PARENB;
	newtio->c_cflag &= ~CSTOPB;
	newtio->c_cflag &= ~CSIZE;
	newtio->c_cflag |= CS8;
    }
    if(strncmp(CharParity,"7E1",3)==0)
    {
	newtio->c_cflag |= PARENB;
	newtio->c_cflag &= ~PARODD;
	newtio->c_cflag &= ~CSTOPB;
	newtio->c_cflag &= ~CSIZE;
	newtio->c_cflag |= CS7;
    }
    if(strncmp(CharParity,"7O1",3)==0)
    {
	newtio->c_cflag |= PARENB;
	newtio->c_cflag |= PARODD;
	newtio->c_cflag &= ~CSTOPB;
	newtio->c_cflag &= ~CSIZE;
	newtio->c_cflag |= CS7;
    }
    if(strncmp(CharParity,"7S1",3)==0)
    {
	newtio->c_cflag &= ~PARENB;
	newtio->c_cflag &= ~CSTOPB;
	newtio->c_cflag &= ~CSIZE;
	newtio->c_cflag |= CS8;
    }

    // IGNPAR  : ignora los bytes con error de paridad
    // ICRNL   : mapea CR a NL (en otro caso una entrada CR del otro ordenador
    // no terminaria la entrada) en otro caso hace un dispositivo en bruto
    // (sin otro proceso de entrada)
    newtio->c_iflag = 0;
    //newtio->c_iflag = IGNPAR;
    //newtio->c_iflag |= ICRNL;

    //Salida en bruto.
    newtio->c_oflag = 0;

    //ICANON  : activa entrada canonica(Modo texto)
    //desactiva todas las funcionalidades del eco, y no envia segnales al
    //programa llamador
    //newtio->c_lflag = ICANON;
    newtio->c_lflag = 0;

    // inicializa todos los caracteres de control
    // los valores por defecto se pueden encontrar en /usr/include/termios.h,
    // y vienen dadas en los comentarios, pero no los necesitamos aqui

    newtio->c_cc[VTIME]    = 20;     /* temporizador entre caracter, no usado */
    newtio->c_cc[VMIN]     = 5;     /* bloqu.lectura hasta llegada de caracter. 1 */
	

	
    if(tcsetattr(fd,TCSANOW,newtio)!=0)
    {
	printf("ERROR: No se pudo poner Configuracion del Puerto\n" );
	return false;
    }
     
    return true;
}
    
    
/** \var int Set_Configure_Port(int fd,struct termios Porttermio)
 *  \brief Coloca la configuracion en el puerto serie
 *  \param fd Es el manejador del puerto 
 *  \param Porttermio es la configuracion del puerto
 *  \return true si todo fue bien o false si no 
 *  \ingroup HeaderLinux
 */  
bool Set_Configure_Port(int fd,struct termios newtio)
{
    // ahora limpiamos el buffer de entrada y salidadel modem y activamos 
    // la configuracion del puerto
    //tcflush(fd, TCIOFLUSH);
	
    if(tcsetattr(fd,TCSANOW,&newtio)!=0)
    {
	printf("ERROR: No se pudo poner Comfiguracion del Puerto\n" );
	return false;
    }
	
    return true;
}

    
/** \var long Gets_Port(int fd,char *Data,int SizeData)
 *  \brief Recibe datos en el puerto serie,lee hasta encontrar un 0x0A,0x0D  
 *         (rellenando el siguiente byte con un cero - "solo si existe")   
 *         o hasta completar SizeData caracteres.
 *  \param fd Es el manejador del puerto 
 *  \param Data Es el arreglo donde se almacenarán los datos recibidos
 *  \param SizeData es el tamaño maximo del arreglo
 *  \return En caso de éxito, se devuelve el número de bytes recibidos (cero 
 *          indica que no se ha recibido nada). En caso de error, se devuelve -1 
 *  \ingroup HeaderLinux    
 */
long Gets_Port(int fd,char *Data,int SizeData)
{
    long n=0,i=0;

    for(i=0;i<SizeData;i++)
    {
	read(fd,&Data[i],1);
	if((Data[i]==13)||(Data[i]==10)&&(i!=0))
	{
	    n=i+1;
	    if(n<SizeData) Data[n]=0;
	    i=SizeData;
	}
    }
         
    return n;
}
    
/** \var Getc_Port(int fd,char *Data)
 *  \brief Recibe un caracter en el puerto serie.
 *  \param fd Es el manejador del puerto 
 *  \param Data Es el dato(8-bit) a mandar
 *  \return En caso de éxito, se devuelve el número de bytes recibidos (cero 
 *          indica que no se ha recibido nada). En caso de error, se devuelve -1 
 *  \ingroup HeaderLinux    
 */    
long Getc_Port(int fd,char *Data)
{
    long n;

    n=read(fd,Data,1);
        
    return n;
}


/** \var int Kbhit_Port(int fd)
 *  \brief Indica el numero de caracteres disponible en el buffer de entrada.
 *  \param fd Es el manejador del puerto. 
 *  \return El numero de caracteres en el buffer de recepcion.
 *  \ingroup HeaderLinux    
 */   
int Kbhit_Port(int fd)
{
    int bytes;
    ioctl(fd, FIONREAD, &bytes);
    return bytes;

}
    

/** \var int Close_Port(int hComm)
 *  \brief Cierra el puerto serie.
 *  \param fd Es el manejador del puerto. 
 *  \return true si se ha cerrado el Puerto y false en el caso contrario.
 *  \ingroup HeaderLinux    
 */    
bool Close_Port(int fd)
{
    if (fd != INVALID_HANDLE_VALUE)
    {  // Close the communication port.
	// ahora limpiamos el buffer de entrada y salidadel modem y activamos 
	// la configuracion del puerto
	//tcflush(fd, TCIOFLUSH);
	if (close(fd)!=0)
	{
	    printf("Error cerrando archivo\n");
	    return false;
	}
	else
	{
	    fd = INVALID_HANDLE_VALUE;
	    return true;
	}
    }
    return false;
}


/** \var int Set_Hands_Haking(int fd,int FlowControl)
 *  \brief Configura el control de flujo en el puerto serie
 *  \param fd Es el manejador del puerto. 
 *  \param FlowControl 
 *                     0    Ninguno<br>                                            
 *                     1    RTS/CTS<br>                                             
 *                     2    Xon/Xoff<br>                                            
 *                     3    DTR/DSR  
 *  \return true si todo fue bien y false si no lo fue.
 *  \ingroup HeaderLinux    
 */    
bool Set_Hands_Haking(int fd,int FlowControl)
{
    struct termios newtio;
    tcgetattr(fd,&newtio);  /* almacenamos la configuracion actual del puerto */
    switch (FlowControl)
    {
    case 0://NONE
	{
	    newtio.c_cflag &= ~CRTSCTS;
	    newtio.c_iflag &= ~(IXON | IXOFF | IXANY);
	    newtio.c_cc[VSTART]   = 0;     /* Ctrl-q */
	    newtio.c_cc[VSTOP]    = 0;     /* Ctrl-s */
	    break;
	}
    case 1://RTS/CTS - HARD
	{
	    newtio.c_cflag |= CRTSCTS;
	    newtio.c_iflag &= ~(IXON | IXOFF | IXANY);
	    newtio.c_cc[VSTART]   = 0;     /* Ctrl-q */
	    newtio.c_cc[VSTOP]    = 0;     /* Ctrl-s */
	    break;
	}
    case 2://XON/XOFF - SOFT
	{
	    newtio.c_cflag &= ~CRTSCTS;
	    newtio.c_iflag |= (IXON | IXOFF );//| IXANY);
	    newtio.c_cc[VSTART]   = 17;     /* Ctrl-q */
	    newtio.c_cc[VSTOP]    = 19;     /* Ctrl-s */
	    break;
	}
    }
    tcsetattr(fd, TCSANOW, &newtio);
    return true;
}


/** \var int Set_RThreshold(int fd,int n)
 *  \brief configura el numero minimo de caracteres que permitira
 *         que se ejecute la lectura del puerto
 *  \param fd Es el manejador del puerto. 
 *  \param n  es el numero de caracteres que activara la lectura
 *  \return true si todo fue bien y false si no lo fue.
 *  \ingroup HeaderLinux    
 */        
bool Set_RThreshold(int fd,int n)
{
    return true;
}
    
/** \var bool Set_BaudRate(int fd,unsigned int BaudRate)
 *  \brief configura la  velocidad puerto serie
 *  \param fd Es el manejador del puerto. 
 *  \param BaudRate Es la velocidad del PUerto
 *  \return true si todo fue bien y false si no lo fue.
 *  \ingroup HeaderLinux    
 */    
bool Set_BaudRate(int fd,unsigned int BaudRate)
{
    struct termios newtio;

    if(tcgetattr(fd,&newtio)!=0)
    {
	printf("Error obteniendo configuracion time-out actual\n");
	return false;
    }
	
    cfsetispeed(&newtio, BaudRate);
    cfsetospeed(&newtio, BaudRate);
	
    if(tcsetattr(fd, TCSANOW, &newtio)!=0)
    {
	printf("Error estableciendo nueva configuracion time-out\n");
	return false;
    }
	
    return true;	
}
	

/** \var bool Set_Time(int fd,unsigned int Time, unsigned int chars)
 *  \brief configura Temporizador para read y write
 *  \param fd Es el manejador del puerto. 
 *  \param Time Tiempo entre bits, T=Time*0.1s, para tamaño total de time-out   
 *               en read y write.<br>
 *               Timeout = (Time *0.1* number_of_bytes) seg 
 *  \params chars Numero de caracteres a leer antes de volver del read.
 *  \return true si todo fue bien y false si no lo fue.
 *  \ingroup HeaderLinux    
 */  
bool Set_Time(int fd,unsigned int Time, unsigned int chars)
{
    struct termios newtio;
    /* almacenamos la configuracion actual del puerto */
    if(tcgetattr(fd,&newtio)!=0)
    {
	printf("Error obteniendo configuracion time-out actual\n");
	return false;
    }
        
    newtio.c_cc[VTIME]    = Time;/*temporizador entre caracter*/
    newtio.c_cc[VMIN]     = chars;   /*bloquea lectura hasta llegada de 
				   caracter 1 */
	
    if(tcsetattr(fd, TCSANOW, &newtio)!=0)
    {
	printf("Error estableciendo nueva configuracion time-out\n");
	return false;
    }
	
    return true;	
}
    

/** \var bool Clean_Buffer(int fd)
 *  \brief Termina las operaciones de lectura y escritura pendientes y limpia
 *         las colas de recepción y de transmisión.
 *  \param fd Es el manejador del puerto. 
 *  \return true si todo fue bien y false si no lo fue.
 *  \ingroup HeaderLinux    
 */  
bool Clean_Buffer(int fd) 
{
    if(tcflush(fd, TCIOFLUSH)!=0)
    {
	printf("Error Limpiando el Buffer  de entrada y salida\n");
	return false;
    }
    return true;
}
    
/** \var bool Setup_Comm(int fd,unsigned long InQueue,unsigned long OutQueue) 
 *  \brief Especifica el tamaño en Bytes del buffer de entrada y salida
 *  \param fd Es el manejador del puerto. 
 *  \param InQueue Especifica el tamaño en Bytes del buffer de entrada, se 
 *                 Recomienda el uso de numero pares.
 *  \param OutQueue Especifica el tamaño en Bytes del buffer de salida, se 
 *                 Recomienda el uso de numero pares.
 *  \return true si todo fue bien y false si no lo fue.
 *      
 */  
bool Setup_Buffer(int fd,unsigned long InQueue,unsigned long OutQueue) 
{
    return true;
}
    
/** \var long Write_Port(int fd,char Data[],int SizeData)
 *  \brief Escribe en el puerto serie
 *  \param fd Es el manejador del puerto 
 *  \param Data Es el dato a mandar
 *  \param SizeData es el tamaño del dato
 *  \return En caso de éxito, se devuelve el número de bytes escritos (cero 
 *          indica que no se ha escrito nada). En caso de error, se devuelve -1 
 *  \ingroup HeaderLinux
 */
long Write_Port(int fd,unsigned char Data[],int SizeData)
{
    return  write(fd,Data,SizeData);
}
    
    
/** \var long Read_Port(int fd,char *Data,int SizeData)
 *  \brief Recibe datos en el puerto serie
 *  \param fd Es el manejador del puerto 
 *  \param Data Es el arreglo donde se almacenarán los datos recibidos
 *  \param SizeData es el tamaño del arreglo
 *  \return En caso de éxito, se devuelve el número de bytes recibidos (cero 
 *          indica que no se ha recibido nada). En caso de error, se devuelve -1 
 *  \ingroup HeaderLinux
 */  
long Read_Port(int fd,unsigned char *Data,int SizeData)
{
    return read(fd,Data,SizeData);
}  
  
